#pragma once

#include <iostream>
#include <string>
#include "InetAddr.hpp"
#include "Log.hpp"


namespace SocketModel{
    using namespace LogModel;

    class Socket{
        public:
            ~Socket(){};
            virtual void SocketOrDie() = 0;
            virtual void BindOrDie(uint16_t port) = 0;
            virtual void ListenOrDie(int backlog) = 0;
            virtual std::shared_ptr<Socket> AcceptOrDie(InetAddr *client) = 0;
            virtual void Close() = 0;
            virtual int Recv(std::string &out) = 0;
            virtual int Send(std::string &data) = 0;
            virtual int Connect(uint16_t port,std::string ip) = 0;
            void BuildTcpSocket(uint16_t port,int backlog = 10){
                SocketOrDie();
                BindOrDie(port);
                ListenOrDie(backlog);
            }            

    };
    const static int defaultfd = -1;
    class TcpSocket:public Socket{
        public:
            TcpSocket(int fd = defaultfd):_sockfd(fd){}
            //获取sockfd
            void SocketOrDie() override{
                _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
                if(_sockfd < 0){
                    Log(LogLevel::FATAL)<<"SOCKET err";
                    exit(SOCKET_ERR);
                }
                Log(LogLevel::INFO)<<"SOCKET success";
            }

            void BindOrDie(uint16_t port) override{
                InetAddr localaddr(port);
                int n = ::bind(_sockfd,localaddr.NetAddrPtr(),localaddr.NetAddrlen());
                if(n<0){
                    Log(LogLevel::FATAL)<<"Bind erro";
                    exit(BIND_ERR);
                }
                Log(LogLevel::INFO)<<"Bind success";
            }
            void ListenOrDie(int backlog) override
            {
                int n = listen(_sockfd,backlog);
                if(n<0){
                    Log(LogLevel::FATAL)<<"Listen erro";
                    exit(LISTEN_ERR);
                }
                Log(LogLevel::INFO)<<"LISTEN success";
            }

            std::shared_ptr<Socket> AcceptOrDie(InetAddr *client) override{
                sockaddr_in peer;
                memset(&peer,0,sizeof(peer));
                socklen_t len = sizeof(peer);
                int n_fd = ::accept(_sockfd,CONV(peer),&len);
                if(n_fd < 0)
                {
                    Log(LogLevel::WARNING) << "accept warning ...";
                    return nullptr; // TODO
                }   
                client->SetAddr(peer);
                return std::make_shared<TcpSocket>(n_fd);
            }
            //从网络获取数据
            int Recv(std::string &out) override {
                //流式读取
                char Buff[1024];
                int n = ::recv(_sockfd,Buff,sizeof(Buff)-1,0);//减一是为了加\0
                if(n>0){
                    Buff[n] = 0;
                    out += Buff;
                }
                return n;
            }
            //将序列化的数据发送到网络
            int Send(std::string &data) override{
                return ::send(_sockfd,data.c_str(),data.size(),0);

            }
            //客户端连接服务端
            int Connect(uint16_t port,std::string ip) override{
                InetAddr sever(ip,port);
                return ::connect(_sockfd,sever.NetAddrPtr(),sever.NetAddrlen());

            }

            void Close()override{
                if(_sockfd>=0) ::close(_sockfd);
            }
        private: 
        int _sockfd;
    };   
}